package com.wzapp.sitposture.authentication.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import com.wzapp.sitposture.authentication.pojo.entity.Oss;
import com.wzapp.sitposture.authentication.pojo.param.ValidMobileParam;
import com.wzapp.sitposture.authentication.pojo.view.*;
import com.wzapp.sitposture.authentication.repository.OssRepository;
import com.wzapp.sitposture.authentication.service.AuthenticationService;
import com.wzapp.sitposture.authentication.service.MobileAndCodeService;
import com.wzapp.sitposture.authentication.util.SmsUtil;
import com.wzapp.sitposture.common.constant.AuthenticationConst;
import com.wzapp.sitposture.common.error.CustomizedException;
import com.wzapp.sitposture.common.util.DateUtil;
import com.wzapp.sitposture.common.util.MD5Util;
import com.wzapp.sitposture.common.util.ToolUtil;
import com.wzapp.sitposture.config.redis.service.RedisService;
import com.wzapp.sitposture.config.redission.annotation.RedisLock;
import com.wzapp.sitposture.setup.service.SetupService;
import com.wzapp.sitposture.usercenter.service.UserService;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by qianglong on 2018/1/23.
 */
@Service
public class AuthenticationServiceImpl implements AuthenticationService {

    private static final Logger log = LoggerFactory.getLogger(AuthenticationServiceImpl.class);

//    private static final Object LOCK = new Object();

    @Autowired
    OssRepository ossRepository;

    @Autowired
    UserService userService;

    @Autowired
    MobileAndCodeService mobileAndCodeService;

    @Autowired
    Environment environment;
    @Autowired
    RedisService redisService;

    @Autowired
    SetupService setupService;


    //七牛ACCESS_KEY
    @Value("${ql.oss.qiniuAccessKey}")
    private String qiniuAccessKey;
    //七牛SECRET_KEY
    @Value("${ql.oss.qiniuSecretKey}")
    private String qiniuSecretKey;

    // TOP分配给应用的AppKey
    @Value("${ql.ali.sms.appKey}")
    private String appKey;

    // 短信签名AppKey对应的secret值
    @Value("${ql.ali.sms.appSecret}")
    private String appSecret;

    //短信获取最大次数，正常5次
    @Value("${ql.sms.code.max.count}")
    private Integer smsMaxCount;

    @Value("${spring.profiles.active}")
    private String profile;

    @Override
    public List<OssDTO> requestOss(OssRequest ossRequest) throws CustomizedException {
        List<Oss> list = ossRepository.findAll();
        if (ToolUtil.isEmpty(list)) {
            throw new CustomizedException("缺少OSS配置数据");
        }

        String key = null;
        String type = null;
        Integer width = null;
        Integer height = null;
        if (ToolUtil.isNotEmpty(ossRequest)) {
            key = ossRequest.getKey();
            type = ossRequest.getType();
            width = ossRequest.getWidth();
            height = ossRequest.getHeight();
            log.debug("==============key====================" + key);
        }
        boolean imageslim = setupService.getSwitch("imageslim");
        List<OssDTO> ossDTOS = new ArrayList<>(list.size());
        for (Oss oss : list) {
            OssDTO dto = new OssDTO();
            BeanUtils.copyProperties(oss, dto);

            String ossId = dto.getId();
            Bucket image = dto.getImage();
            if(imageslim) {
                image.setToken(this.requestImageslimToken(ossId, image.getBucket(), key));
            }else {
                image.setToken(this.requestDefaultToken(ossId, image.getBucket(), null));
            }
            ossDTOS.add(dto);
            log.info("==================imageToken:" + image.toString());
        }

        return ossDTOS;
    }

    @Override
    @RedisLock(key = "AuthenticationServiceImpl.fetchByMobile",value = "#mobile")
    public SmsCode fetchByMobile(String mobile, String type) throws Exception {

        validMobile(mobile, type);

        Long current = System.currentTimeMillis();

        long expireTime = AuthenticationConst.CODE_DELETETIME * 1000;

        //验证是否可以发送验证码
        validCanObtainMobileCode(mobile,current,expireTime);

        SmsUtil smsUtil = new SmsUtil();
        String verifyCode = ToolUtil.getRandomNum(6);

        JSONObject params = new JSONObject();
        params.put("code", verifyCode);

        boolean result = true;
        //只有生产环境和测试环境需要发送短信
        if ("prod".equals(profile) || "dev".equals(profile)) {
            // 发送短信验证码
            result = smsUtil.sendSms(mobile, appKey, appSecret, params);

            if (!result) {
                throw new CustomizedException("authentication.sms.send.error");
            }
        }

        // 验证码保存到数据库，5分钟过期
        String verifyCodeMD5 = MD5Util.encrypt(verifyCode);
        mobileAndCodeService.storage(AuthenticationConst.REDIS_CODE_PREFIX + mobile, verifyCodeMD5, current + expireTime);
        SmsCode smsCode = new SmsCode();
        smsCode.setCode(verifyCodeMD5);
        smsCode.setMobile(mobile);
        smsCode.setExpireTime(expireTime);
        return smsCode;
    }

    private void validMobile(String mobile, String type) throws CustomizedException {

        switch (type) {
                //注册时手机号码必须未注册
            case AuthenticationConst.FETCHCODE_TYPE_REGISTER:
                if (userService.isExists(mobile)) {
                    throw new CustomizedException("authentication.mobile.exists.error");
                }
                break;
                //找回密码时手机号码必须已注册
            case AuthenticationConst.FETCHCODE_TYPE_PASSWORD:
                if (!userService.isExists(mobile)) {
                    throw new CustomizedException("authentication.mobile.notExists.error");
                }
                break;
            default:
                throw new CustomizedException("public.param.error", "不支持类型：" + type);
        }
    }

    /**
     * 功能: 验证手机号 <br/>
     *
     * @param validMobileParam 手机号验证参数
     * @return Boolean
     * @author ql
     * @create 2018/5/7 19:47
     **/
    @Override
    public Boolean validMobileCode(ValidMobileParam validMobileParam) throws Exception {

        //必须是已经注册的手机号
        String mobile = validMobileParam.getMobile();
        String code = validMobileParam.getCode();
        if (!userService.isExists(mobile)) {
            throw new CustomizedException("authentication.mobile.notExists.error");
        }
        //如果非法则报错
        if (!verifyCode(mobile, code)) {
            throw new CustomizedException("userCenter.code.is.error");
        }

        //设置手机号验证状态为true 存储状态为15分钟
        redisService.set(AuthenticationConst.REDIS_MOBILE_VALID_PREFIX + mobile, true, 900L);
        return true;
    }

    /**
     * 校验验证码
     *
     * @param mobile 手机号码<br/>
     * @param code   验证码<br/>
     * @return
     */
    @Override
    public boolean verifyCode(String mobile, String code) {

        if (ToolUtil.isEmpty(code)) {
            return false;
        }
        // 从数据库中获取验证码，进行验证码比对，错误的话返回失败
        String codeInRedis = mobileAndCodeService.queryMobileCodeByKey(AuthenticationConst.REDIS_CODE_PREFIX + mobile);
        log.debug("----codeInRedis:" + codeInRedis);
        String codeMd5 = MD5Util.encrypt(code);
        log.debug("--------codeMd5:" + codeMd5);

        if (codeMd5.equals(codeInRedis)) {
            // 验证成功后删除数据库中的验证码
//            mobileAndCodeService.remove(AuthenticationConst.REDIS_CODE_PREFIX + mobile);

            return true;
        }
        return false;
    }

    @Override
    public void removeRedisCode(String mobile) {
        // 验证成功后删除数据库中的验证码
        mobileAndCodeService.remove(AuthenticationConst.REDIS_CODE_PREFIX + mobile);
    }

    private void validCanObtainMobileCode(String mobile, Long current,Long limitTime) throws CustomizedException {

//        synchronized (LOCK) {
        // 先检查是否在短时间内获取过验证码
        Long expireTime = mobileAndCodeService.getExpireTime(AuthenticationConst.REDIS_CODE_PREFIX + mobile);
        if (ToolUtil.isNotEmpty(expireTime)
                && expireTime - limitTime + AuthenticationConst.CODE_EXPIRETIME * 1000 > current) {
            throw new CustomizedException("authentication.code.repeat.error", AuthenticationConst.CODE_EXPIRETIME + "");
        }

        Integer count = mobileAndCodeService.count(AuthenticationConst.REDIS_CODE_PREFIX + mobile, DateUtil
                .getStartTime(), DateUtil.getEndTime());

        //一天内获取短信验证码不能超过{0}次！
        if (count >= smsMaxCount) {
            throw new CustomizedException("authentication.code.count.error", smsMaxCount + "");
        }
//        }
    }

    //图片瘦身压缩
    private Token requestImageslimToken(String id, String bucket, String key) {

        if (!AuthenticationConst.OSS_QINIU.equals(id)) {
            return null;
        }

        long deadline = System.currentTimeMillis() / 1000 + AuthenticationConst.TOKEN_EXPIRETIME;
        StringMap policy = new StringMap();
        //图片瘦身
        String persistentOps = "imageslim";

        //saveas用来用转码后的文件覆盖转码前的文件
        String entry = bucket + ":" + key;
        String encodedEntryURI = Base64.encodeBase64String(entry.getBytes(Charset.forName("UTF-8")));
        log.debug("===================saveas===============" + encodedEntryURI);
        persistentOps = persistentOps + "|saveas/" + encodedEntryURI;

        policy.put("persistentOps", persistentOps);
//        String domain = environment.getProperty("ql.domain");
//        policy.put("persistentNotifyUrl", domain + "/notify/videoupload_qn");
//        policy.put("persistentPipeline", "everygod");
        String tokenValue = Auth.create(qiniuAccessKey, qiniuSecretKey).uploadTokenWithDeadline(bucket, key, deadline, policy, true);

        Token t = new Token();
        t.setToken(tokenValue);
        t.setExpireTime(AuthenticationConst.TOKEN_EXPIRETIME * 1000);

        return t;
    }

    private Token requestDefaultToken(String id, String bucket, String key) {

        if (AuthenticationConst.OSS_QINIU.equals(id)) {
            long deadline = System.currentTimeMillis() / 1000 + AuthenticationConst.TOKEN_EXPIRETIME;
            StringMap policy = new StringMap();
            //wmImage后面是水印的外网地址base64
            String persistentOps = "avthumb/mp4/ab/160k/ar" +
                    "/44100/acodec/libfaac/r/30/vb/2400k/vcodec/libx264/s/1280x720/autoscale/1/stripmeta/0";
            //saveas用来用转码后的文件覆盖转码前的文件
            if (ToolUtil.isNotEmpty(key)) {
                String entry = bucket + ":" + key;
                String encodedEntryURI = Base64.encodeBase64String(entry.getBytes(Charset.forName("UTF-8")));
                log.debug("===================saveas===============" + encodedEntryURI);
                persistentOps = persistentOps + "|saveas/" + encodedEntryURI;
            }

            //";avthumb/mp3/vn/1|saveas/" + encodedEntryURI; 转码后只保留音频，去掉视频
            //七牛技术人员：视频转码时长是总时长的1/3-1/5时间浮动
            policy.put("persistentOps", persistentOps);
//            String domain = environment.getProperty("ql.domain");
//            policy.put("persistentNotifyUrl", domain + "/notify/videoupload_qn");
//            policy.put("persistentPipeline", "everygod");
            String tokenValue = Auth.create(qiniuAccessKey, qiniuSecretKey).uploadTokenWithDeadline(bucket, key, deadline, policy, true);

            Token t = new Token();
            t.setToken(tokenValue);
            t.setExpireTime(AuthenticationConst.TOKEN_EXPIRETIME * 1000);
            return t;
        }
        return null;
    }

}
